package control;

import java.util.ArrayList;
import java.util.List;

import alto_nivel.gals.LexicalError;
import alto_nivel.gals.Lexico;
import alto_nivel.gals.SemanticError;
import alto_nivel.gals.SemanticoAltoNivel;
import alto_nivel.gals.Sintatico;
import alto_nivel.gals.SyntaticError;
import alto_nivel.gals.Token;

public class ControleAltoNivel {

    private List<NotificaTela> listeners = new ArrayList<NotificaTela>();
    private Lexico lexico = new Lexico();
    private Sintatico sintatico = new Sintatico();
    private SemanticoAltoNivel semantico = new SemanticoAltoNivel();
    private int numeroLinha = 1;
    private String conteudo;

    public void setListener(NotificaTela listenerObj) {
        listeners.add(listenerObj);
    }

    public void removeListener(NotificaTela listenerObj) {
        listeners.remove(listenerObj);
    }

    private void notificarListenersSucesso(String analise) {
        for (NotificaTela list : listeners) {
            list.saidaConsoleSucesso(analise);
        }
    }

    private void notificarListenersErro(String analise) {
        for (NotificaTela list : listeners) {
            list.saidaConsoleErro(analise);
        }
    }

    private void notificarListenersLimpaConsole() {
        for (NotificaTela list : listeners) {
            list.limpaTxConsole();
        }
    }

    public void analisar() {

        lexico.setInput(conteudo);

        semantico = new SemanticoAltoNivel();

        notificarListenersLimpaConsole();

        try {

            sintatico.parse(lexico, semantico);

            notificarListenersSucesso("\t\tPrograma compilado com sucesso!");

        } catch (LexicalError e) {

            buscaLinha(e.getPosition());

            if (e.getMessage().equals("símbolo inválido")) {

                char invalid = buscaSimboloInvalido(e.getPosition());

                notificarListenersErro("Erro na linha " + numeroLinha + " - " + invalid + " " + e.getMessage());

            } else {

                notificarListenersErro("Erro na linha " + numeroLinha + " - " + e.getMessage());
            }

            resetaLinha();

        } catch (SyntaticError e) {

            buscaLinha(e.getPosition());

            String tipo = determinaTipo(sintatico.getCurrentToken());

            notificarListenersErro("Erro sintático na linha  " + numeroLinha + ", " + " encontrado " + tipo + " " + sintatico.getCurrentTokenString() + ", " + e.getMessage());

            resetaLinha();

        } catch (SemanticError e) {
            buscaLinha(e.getPosition());
            notificarListenersErro("Erro na linha " + numeroLinha + " - " + e.getMessage());
            resetaLinha();
        }
    }

    public List<String> gerarCodigo() {
	for (String s : semantico.getLinguagemObjeto()) {
	    System.out.println(s);
	}
        return semantico.getLinguagemObjeto();
    }

    private String determinaTipo(Token currentToken) {

        switch (currentToken.getId()) {

            case 2:
                return "identificador";

            case 3:
                return "constante inteira";

            case 4:
                return "palavra reservada inválida";
                
            case 5:
        	return "nome de programa inválido";
        	
            default:

                if (currentToken.getId() > 5 && currentToken.getId() < 11) {
                    return "palavra reservada";
                }

                if (currentToken.getId() > 11) {
                    return "símbolo especial";
                }
        }
        return "";

    }

    private char buscaSimboloInvalido(int position) {
        return conteudo.charAt(position);
    }

    private void buscaLinha(int position) {

        for (int i = 0; i <= position; i++) {

            if (conteudo.length() == i || conteudo.length() < i) {
                return;
            }

            if (conteudo.charAt(i) == '\n' && i == position) {
                return;
            } else if (conteudo.charAt(i) == '\n') {
                numeroLinha++;
            }
        }
    }

    private void resetaLinha() {
        numeroLinha = 1;
    }

    public void setConteudo(String conteudo) {
        this.conteudo = conteudo;
    }

    public SemanticoAltoNivel getSemantico() {
        return semantico;
    }

    public String getNomePrograma() {
	return semantico.getNomePrograma();
    }
    
    public String construirHeader() {
	return semantico.construirHeader();
    }

}
